Skip to content

feat(compute): add precision option to preserve sub-cent integer amounts#281

Open
anttiviljami wants to merge 1 commit into
mainfrom
claude/zealous-archimedes-HQ6Py
Open

feat(compute): add precision option to preserve sub-cent integer amounts#281
anttiviljami wants to merge 1 commit into
mainfrom
claude/zealous-archimedes-HQ6Py

Conversation

@anttiviljami
Copy link
Copy Markdown
Member

Summary

Add an opt-in precision option to computeAggregatedAndPriceTotals so consumers can preserve sub-cent precision in the integer amount fields (amount_total, unit_amount, unit_amount_gross, etc.).

// Default — current behaviour, integers rounded to whole cents
computeAggregatedAndPriceTotals(items)
//   → amount_total: 15, amount_total_decimal: '0.1524'

// Opt-in — integers carry the full DECIMAL_PRECISION (12)
computeAggregatedAndPriceTotals(items, { precision: 12 })
//   → amount_total: 152_400_000_000, amount_total_decimal: '0.1524'

Why

Per-unit tariff rates are routinely configured at sub-cent precision — e.g. 0.1524 EUR/kWh for an Arbeitspreis Energie line, or 0.00352 EUR/kWh for a small grid-loss surcharge. Today the engine rounds the integer fields to whole cents on the way out (amount_total: 15, amount_total: 0), so any consumer that doesn't fall back to the *_decimal string fields renders the rates as 15 cents / €0 / 1 cent, in conflict with the contracted tariff sheet and visibly inconsistent with the per-unit detail line displayed beside them. A team-reported reproducer is in STABLE360-11491.

The *_decimal string fields are already preserved at full precision today and consumers can recover precision through them, but every consumer has to remember to opt in via shouldShowDecimals-style heuristics. Exposing precision as a first-class option closes the loop: callers that need full precision (variable per-unit price displays, ECP charts, customer-facing tariff breakdowns) just request it on the compute call and receive consistent integer + decimal data.

What changes

  • New optional precision?: number on ComputeAggregatedAndPriceTotalsOptions. Default DEFAULT_INTEGER_AMOUNT_PRECISION (2).
  • Threaded into the three internal convert*Precision calls (convertPriceComponentsPrecision, convertPriceItemPrecision, convertPricingPrecision) at compute-totals.ts:90/126/145.
  • Three new tests verify (a) the default rounds to cents (parity with current fixtures), (b) precision: 12 preserves 0.1524 EUR as 152_400_000_000 integer cents, (c) sub-cent amounts (0.00352) no longer collapse to 0.
  • A changeset is included (minor bump — additive, opt-in, no breaking changes).

Backwards compatibility

No behavioural change for any existing caller. All 634 existing tests pass without modification. The *_decimal string fields continue to be populated at full precision regardless of the new option, so a consumer that was already reading them sees no diff.

Risk

  • The integer-precision-12 path produces large numbers (e.g. 152_400_000_000). Consumers that explicitly opt in must be able to handle these; Number.MAX_SAFE_INTEGER is 9_007_199_254_740_992, so a single line item up to ~€90M still fits comfortably and aggregate totals on consumer carts shouldn't approach that ceiling. Documented in the JSDoc.
  • Consumers that mix opt-in and default must not pass a precision-12 line item into a system expecting cents. This is a knowing opt-in, not a default switch.

Test plan

  • pnpm test — 634 + 3 new = 637 tests passing locally
  • tsc --noEmit — clean
  • pnpm lint — no new warnings introduced (existing warnings on non-null assertions are pre-existing)
  • pnpm build — clean

Closes STABLE360-11491 on the engine side. The journey-monorepo can plug in via getPricingDetailsFormatted later (separate MR).

🤖 Generated with Claude Opus 4.7

Per-unit rates such as `0.1524 EUR/kWh` and sub-cent rates like
`0.00352 EUR/kWh` are configured by tariff operators with full decimal
precision but the engine's `amount_total` / `unit_amount` integer fields
are rounded to whole cents, which downstream consumers display as
`15 cents` / `€0` / `1 cent` even when the contracted rate is something
else. Today consumers can read the `*_decimal` string fields to recover
precision but every consumer has to remember to do so.

Add an opt-in `precision` option to `computeAggregatedAndPriceTotals`,
defaulting to `DEFAULT_INTEGER_AMOUNT_PRECISION` (2). Callers that need
to render or compute on sub-cent rates can pass
`{ precision: 12 }` (i.e. `DECIMAL_PRECISION`) and receive integer
fields at the same precision the dinero math is done at, with the
`*_decimal` strings unchanged.

The option is threaded through the three internal `convert*Precision`
calls. Default behaviour for every existing caller is identical — all
634 existing tests pass without modification — and three new tests
cover the opt-in path against `0.1524 EUR/kWh` and `0.00352 EUR/kWh`.

Refs https://e-pilot.atlassian.net/browse/STABLE360-11491

Co-authored-by: Claude <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 5, 2026

🦋 Changeset detected

Latest commit: 852d568

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@epilot/pricing Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant